#if !defined(_HGTRANSFORMATIONMATRIX_H_)
#define _HGTRANSFORMATIONMATRIX_H_

/**
  * @author PauK
  * @param T must implements copy constructor and such operations as ...
  * @version 1.2a
  * HGTransformationMatrix is a math 4x4 matrix, that always has
  * vector (0, 0, 0, 1) as four row
  * Keep in mind that in this NxM matrix (actually, N = M = 4):
  * N - column count, M - row count.
  ********************************************************************************
  *                             DEBUGGING REQUIRED
  ********************************************************************************
  */

#include "hgmath_global.h"

namespace HUGE
{
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
   * HGTransformationMatrix<T> template interface
   */
 template<typename T>
 class HGTransformationMatrix
 {
  public:
   HGTransformationMatrix(void);
   HGTransformationMatrix(const HGTransformationMatrix<T>& mat);

   explicit HGTransformationMatrix(const T mat[4][3]);
   explicit HGTransformationMatrix(const T &x0, const T &y0, const T &z0,
                                   const T &x1, const T &y1, const T &z1,
                                   const T &x2, const T &y2, const T &z2,
                                   const T &x3, const T &y3, const T &z3);
   explicit HGTransformationMatrix(const HGVector<T> vecs[4]);
   explicit HGTransformationMatrix(const HGVector<T>& vec1, const HGVector<T>& vec2,
                                   const HGVector<T>& vec3, const HGVector<T>& vec4);
   /**
     * Function represents rotation around vector = (x, y, z) by "angle" degrees
     * and saves result in this object.
     * @return HGTransformationMatrix<T>& - Reference to this object
     */
   HGTransformationMatrix<T>& rotate(HGangle angle, const T& x, const T& y, const T& z);

   /**
     * Function represents rotation around vector "vec" by "angle" degrees
     * and saves result in this object.
     * @return HGTransformationMatrix<T>& - Reference to this object
     */
   HGTransformationMatrix<T>& rotate(HGangle angle, const HGVector<T>& vec);

   /**
     * Function represents translation along x, y and z axis
     * and saves result in this object.
     * @return HGTransformationMatrix<T>& - Reference to this object
     */
   HGTransformationMatrix<T>& translate(const T& x, const T& y, const T& z);

   /**
     * Function represents translation and saves result in this object.
     * @return HGTransformationMatrix<T>& - Reference to this object
     */
   HGTransformationMatrix<T>& translate(const HGVector<T>& vec);

   /**
     * Function represents scaling along x, y and z axis with
     * "x", "y" and "z" factor and saves result in this object.
     * @return HGTransformationMatrix<T>& - Reference to this object
     */
   HGTransformationMatrix<T>& scale(const T& x, const T& y, const T& z);

   /**
     * Function represents scaling along x, y and z axis with
     * vec[0], vec[1] and vec[2] factor and saves result in this object.
     * @return HGTransformationMatrix<T>& - Reference to this object
     */
   HGTransformationMatrix<T>& scale(const HGVector<T>& vec);

   /**
     * Simple matrix addition. Result saved in this object.
     * The fourth row (0, 0, 0, 1) will be ignored.
     * @return HGTransformationMatrix<T>& - Reference to this object
     */
   HGTransformationMatrix<T>& add(const HGTransformationMatrix<T> &rhs);

   /**
     * Simple matrix substraction. Result saved in this object.
     * The fourth row (0, 0, 0, 1) will be ignored.
     * @return HGTransformationMatrix<T>& - Reference to this object
     */
   HGTransformationMatrix<T>& sub(const HGTransformationMatrix<T> &rhs);

   /**
     * Matrix-scalar multiplication. Result saved in this object.
     * @return HGTransformationMatrix<T>& - Reference to this object
     */
   HGTransformationMatrix<T>& mul(const T &rhs);

   /**
     * Simple matrix multiplication. Result saved in this object.
     * @return HGTransformationMatrix<T>& - Reference to this object
     */
   HGTransformationMatrix<T>& mul(const HGTransformationMatrix<T> &rhs);

   /**
     * Matrix-vector multiplication. Result saved in this object.
     * @returnHGTransformationMatrix<T>& -  Reference to this object
     */
   HGTransformationMatrix<T>& mul(const HGVector<T>& rhs);

   /**
     * @return HGTransformationMatrix<T>& - Reference to this object
     */
   HGTransformationMatrix<T>& set(const T mat[4][3]);

   /**
     * @return HGTransformationMatrix<T>& - Reference to this object
     */
   HGTransformationMatrix<T>& set(HGint column, const HGVector<T> &vec);

   /**
     * @return HGTransformationMatrix<T>& - Reference to this object
     */
   HGTransformationMatrix<T>& set(HGint column, const T& x,
                                                const T& y,
                                                const T& z);

   /**
     * @return T - Matrix element
     */
   T get(HGint column, HGint row) const;

   /**
     * @return HGVector<T> - Matrix's column "column" as vector
     */
   HGVector<T> operator[](HGint column) const;

   HGTransformationMatrix<T>& operator=(const HGTransformationMatrix<T> &rhs);
   HGbool operator==(const HGTransformationMatrix<T> &rhs) const;

   /**
     * Simple matrix addition.
     * The fourth row (0, 0, 0, 1) will be ignored.
     * @return HGTransformationMatrix<K> - New matrix that contains result
     */
   template<typename K>
   friend HGTransformationMatrix<K> operator+(const HGTransformationMatrix<K> &lhs,
                                              const HGTransformationMatrix<K> &rhs);

   /**
     * Simple matrix substration.
     * The fourth row (0, 0, 0, 1) will be ignored.
     * @return HGTransformationMatrix<K> - New matrix that contains result
     */
   template<typename K>
   friend HGTransformationMatrix<K> operator-(const HGTransformationMatrix<K> &lhs,
                                              const HGTransformationMatrix<K> &rhs);

   /**
     * Simple matrix multiplication.
     * @return HGTransformationMatrix<K> - New matrix that contains result
     */
   template<typename K>
   friend HGTransformationMatrix<K> operator*(const HGTransformationMatrix<K> &lhs,
                                              const HGTransformationMatrix<K> &rhs);

   /**
     * Matrix-vector multiplication.
     * @return HGTransformationMatrix<K> - New matrix that contains result
     */
   template<typename K>
   friend HGTransformationMatrix<K> operator*(const HGTransformationMatrix<K> &lhs,
                                       const HGVector<K>& rhs);

  private:
   void __setMat(const T mat[4][3]);

   T __elems[4][3];
 };
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
   * HGTransformationMatrix<T> template implementation
   */
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename T>
 HGTransformationMatrix<T>::HGTransformationMatrix(void)
 {
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename T>
 HGTransformationMatrix<T>::HGTransformationMatrix(const HGTransformationMatrix<T>& mat)
 {
  __setMat(mat.__elems);
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename T>
 HGTransformationMatrix<T>::HGTransformationMatrix(const T mat[4][3])
 {
  __setMat(mat);
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename T>
 HGTransformationMatrix<T>::HGTransformationMatrix(const T &x0, const T &y0, const T &z0,
                                                   const T &x1, const T &y1, const T &z1,
                                                   const T &x2, const T &y2, const T &z2,
                                                   const T &x3, const T &y3, const T &z3)
 {
  __elems[0][0] = x0; __elems[0][1] = y0; __elems[0][2] = z0;
  __elems[1][0] = x1; __elems[1][1] = y1; __elems[1][2] = z1;
  __elems[2][0] = x2; __elems[2][1] = y2; __elems[2][2] = z2;
  __elems[3][0] = x3; __elems[3][1] = y3; __elems[3][2] = z3;
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename T>
 HGTransformationMatrix<T>::HGTransformationMatrix(const HGVector<T> vecs[4])
 {
  //need to be expanded?
  for(HGint i = 0; i < 4; ++i)
  {
   __elems[i][0] = (vecs[i])[0];
   __elems[i][1] = (vecs[i])[1];
   __elems[i][2] = (vecs[i])[2];
  }
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename T>
 HGTransformationMatrix<T>::HGTransformationMatrix(const HGVector<T>& vec0, const HGVector<T>& vec1,
                                                   const HGVector<T>& vec2, const HGVector<T>& vec3)
 {
  __elems[0][0] = vec0[0]; __elems[0][1] = vec0[1]; __elems[0][2] = vec0[2];
  __elems[1][0] = vec1[0]; __elems[1][1] = vec1[1]; __elems[1][2] = vec1[2];
  __elems[2][0] = vec2[0]; __elems[2][1] = vec2[1]; __elems[2][2] = vec2[2];
  __elems[3][0] = vec3[0]; __elems[3][1] = vec3[1]; __elems[3][2] = vec3[2];
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename T>
 HGTransformationMatrix<T>& HGTransformationMatrix<T>::rotate(HGangle angle, const T& x,
                                                                             const T& y,
                                                                             const T& z)
 {
  //TODO: implement func
  hgAssert(0 /*Not implemented*/);

  return *this;
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename T>
 HGTransformationMatrix<T>& HGTransformationMatrix<T>::rotate(HGangle angle, const HGVector<T>& vec)
 {
  //TODO: implement func
  hgAssert(0 /*Not implemented*/);

  return *this;
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename T>
 HGTransformationMatrix<T>& HGTransformationMatrix<T>::translate(const T& x, const T& y, const T& z)
 {
  //TODO: implement func
  hgAssert(0 /*Not implemented*/);

  return *this;
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename T>
 HGTransformationMatrix<T>& HGTransformationMatrix<T>::translate(const HGVector<T>& vec)
 {
  //TODO: implement func
  hgAssert(0 /*Not implemented*/);

  return *this;
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename T>
 HGTransformationMatrix<T>& HGTransformationMatrix<T>::scale(const T& x, const T& y, const T& z)
 {
  //TODO: implement func
  hgAssert(0 /*Not implemented*/);

  return *this;
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename T>
 HGTransformationMatrix<T>& HGTransformationMatrix<T>::scale(const HGVector<T>& vec)
 {
  //TODO: implement func
  hgAssert(0 /*Not implemented*/);

  return *this;
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename T>
 HGTransformationMatrix<T>& HGTransformationMatrix<T>::add(const HGTransformationMatrix<T> &rhs)
 {
  T *dst = (T *)__elems, *src = (T *)rhs.__elems;
  for(HGint i = 0; i < 12; ++i)
  {
   *dst += *src;
   ++dst; ++src;
  }
  return *this;
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename T>
 HGTransformationMatrix<T>& HGTransformationMatrix<T>::sub(const HGTransformationMatrix<T> &rhs)
 {
  T *dst = (T *)__elems, *src = (T *)rhs.__elems;
  for(HGint i = 0; i < 12; ++i)
  {
   *dst -= *src;
   ++dst; ++src;
  }
  return *this;
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename T>
 HGTransformationMatrix<T>& HGTransformationMatrix<T>::mul(const T &rhs)
 {
  T *dst = (T *)__elems, *src = (T *)rhs.__elems;
  for(HGint i = 0; i < 12; ++i)
  {
   *dst *= *src;
   ++dst; ++src;
  }
  return *this;
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename T>
 HGTransformationMatrix<T>& HGTransformationMatrix<T>::mul(const HGTransformationMatrix<T> &rhs)
 {
  //TODO: implement func
  hgAssert(0 /*Not implemented*/);

  return *this;
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename T>
 HGTransformationMatrix<T>& HGTransformationMatrix<T>::mul(const HGVector<T>& rhs)
 {
  //TODO: implement func
  hgAssert(0 /*Not implemented*/);

  return *this;
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename T>
 HGTransformationMatrix<T>& HGTransformationMatrix<T>::set(const T mat[4][3])
 {
  __setMat(mat);

  return *this;
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename T>
 HGTransformationMatrix<T>& HGTransformationMatrix<T>::set(HGint column, const HGVector<T> &vec)
 {
  hgAssert(column >= 0 && column <= 3);

  __elems[column][0] = vec.x();
  __elems[column][1] = vec.y();
  __elems[column][2] = vec.x();
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename T>
 HGTransformationMatrix<T>& HGTransformationMatrix<T>::set(HGint column, const T& x,
                                                                         const T& y,
                                                                         const T& z)
 {
  hgAssert(column >= 0 && column <= 3);

  __elems[column][0] = x;
  __elems[column][1] = y;
  __elems[column][2] = z;
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename T>
 T HGTransformationMatrix<T>::get(HGint column, HGint row) const
 {
  hgAssert(column >= 0 && row >= 0 && column <= 3 && row <= 3);

  //FIXME: Do we that really need to obtain forth vector's elements (0, 0, 0, 1)?
  if(row == 3)
  {
   if(column == 3)
    return 1;
   return 0;
  }
  return __elems[column][row];
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename T>
 HGVector<T> HGTransformationMatrix<T>::operator[](HGint column) const
 {

  hgAssert(column >= 0 && column <= 3);

  if (column == 3)
   return HGVector<T>(__elems[column][0], __elems[column][1], __elems[column][2], 1);
  else
   return HGVector<T>(__elems[column][0], __elems[column][1], __elems[column][2], 0);
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename T>
 HGTransformationMatrix<T>&
         HGTransformationMatrix<T>::operator=(const HGTransformationMatrix<T> &rhs)
 {
  if(this != *rhs)
   __setMat(rhs.__elems);
  return *this;
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename T>
 HGbool HGTransformationMatrix<T>::operator==(const HGTransformationMatrix<T> &rhs) const
 {
  //FIXME: wtf is goin' on here? Maybe cycle?
  return __elems[0][0] == rhs.__elems[0][0] &&
         __elems[0][1] == rhs.__elems[0][1] &&
         __elems[0][2] == rhs.__elems[0][2] &&

         __elems[1][0] == rhs.__elems[1][0] &&
         __elems[1][1] == rhs.__elems[1][1] &&
         __elems[1][2] == rhs.__elems[1][2] &&

         __elems[2][0] == rhs.__elems[2][0] &&
         __elems[2][1] == rhs.__elems[2][1] &&
         __elems[2][2] == rhs.__elems[2][2] &&

         __elems[3][0] == rhs.__elems[3][0] &&
         __elems[3][1] == rhs.__elems[3][1] &&
         __elems[3][2] == rhs.__elems[3][2];
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename K>
 HGTransformationMatrix<K> operator+(const HGTransformationMatrix<K> &lhs,
                                     const HGTransformationMatrix<K> &rhs)
 {
  //TODO: implement func
  hgAssert(0 /*Not implemented*/);
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename K>
 HGTransformationMatrix<K> operator-(const HGTransformationMatrix<K> &lhs,
                                     const HGTransformationMatrix<K> &rhs)
 {
  //TODO: implement func
  hgAssert(0 /*Not implemented*/);
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename K>
 HGTransformationMatrix<K> operator*(const HGTransformationMatrix<K> &lhs,
                                     const HGTransformationMatrix<K> &rhs)
 {
  //TODO: implement func
  hgAssert(0 /*Not implemented*/);
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename K>
 HGTransformationMatrix<K> operator*(const HGTransformationMatrix<K> &lhs,
                                     const HGVector<K>& rhs)
 {
  //TODO: implement func
  hgAssert(0 /*Not implemented*/);
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 template<typename T>
 void HGTransformationMatrix<T>::__setMat(const T mat[4][3])
 {
  //will we need to expand that cycle?
  T *dst = (T *)__elems, *src = (T *)mat;
  for(HGint i = 0; i < 12; ++i)
  {
   *src = *dst;
   ++src; ++dst;
  }
 }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
}

#endif // _HGTRANSFORMATIONMATRIX_H_
